package com.yly.common.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.util.ParameterizedTypeImpl;
import com.yly.common.response.ResponseVO;
import lombok.extern.log4j.Log4j2;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.util.Timeout;
import org.springframework.util.ObjectUtils;

import java.io.Serializable;
import java.lang.reflect.Type;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author anqinghang
 * Created at 2024/7/3
 */
@Log4j2
public class HttpUtils implements Serializable {

  private static HttpUtils HTTP_UTIL;

  /** 超时时间，以秒为单位 */
  private final int defaultTimeout = 30;

  private PoolingHttpClientConnectionManager manager;

  private CloseableHttpClient client;

  private HttpUtils() {
    init();
  }

  private void init() {
    manager = new PoolingHttpClientConnectionManager();
    manager.setMaxTotal(500);
    manager.setDefaultMaxPerRoute(200);

    final RequestConfig defaultRequestConfig = getRequestConfig(defaultTimeout, defaultTimeout);

    client = HttpClients.custom()
        .setConnectionManager(manager)
        .setDefaultRequestConfig(defaultRequestConfig)
        .build();
  }

  private RequestConfig getRequestConfig(int requestTimeout, int connectTimeout) {
    return RequestConfig.custom()
        .setConnectionRequestTimeout(Timeout.ofSeconds(requestTimeout))
        .setConnectTimeout(Timeout.ofSeconds(connectTimeout))
        .build();
  }

  public static HttpUtils getInstance() {
    if (HTTP_UTIL == null) {
      synchronized (HttpUtils.class) {
        if (HTTP_UTIL == null) {
          HTTP_UTIL = new HttpUtils();
        }
      }
    }
    return HTTP_UTIL;
  }

  public <T> ResponseVO<T> post(String url, Map<String, Object> parameter, Class<T> cla) {
    String dataString = JSON.toJSONString(parameter);
    String result = post(url, defaultTimeout, ContentType.APPLICATION_JSON, dataString);
    if (ObjectUtils.isEmpty(result)) {
      log.warn("post result is null;url:{},data:{}", url, dataString);
    }
    return JSON.parseObject(result, new TypeReference<>(cla) {
    });
  }

  public <T> ResponseVO<List<T>> postList(String url, Map<String, Object> parameter, Class<T> cla) {
    String dataString = JSON.toJSONString(parameter);
    String result = post(url, defaultTimeout, ContentType.APPLICATION_JSON, dataString);
    if (ObjectUtils.isEmpty(result)) {
      log.warn("post result is null;url:{},data:{}", url, dataString);
    }
    ParameterizedTypeImpl inner = new ParameterizedTypeImpl(new Type[]{cla}, null, List.class);
    ParameterizedTypeImpl outer = new ParameterizedTypeImpl(new Type[]{inner}, null, ResponseVO.class);
    return JSON.parseObject(result, outer);
  }

  public String post(String url, String params) {
    return post(url, defaultTimeout, ContentType.APPLICATION_JSON, params);
  }

  public String post(String url, int timeout, ContentType contentType, String data) {
    try {
      HttpPost httpPost = createHttpPost(url, contentType, data);
      httpPost.setConfig(getRequestConfig(timeout, timeout));

      return getResponse(httpPost);
    } catch (Exception e) {
      log.error("exception：url:" + url, e);
    }
    return null;
  }

  public <T> ResponseVO<T> get(String url, Class<T> cla) {
    String result = get(url, defaultTimeout, null);
    if (ObjectUtils.isEmpty(result)) {
      log.warn("get result is null;url:{}", url);
      return null;
    }
    return JSON.parseObject(result, new TypeReference<ResponseVO<T>>(cla) {
    });
  }

  public <T> ResponseVO<List<T>> getList(String url, Class<T> cla) {
    String result = get(url, defaultTimeout, null);
    if (ObjectUtils.isEmpty(result)) {
      log.warn("get result is null;url:{}", url);
      return null;
    }
    ParameterizedTypeImpl inner = new ParameterizedTypeImpl(new Type[]{cla}, null, List.class);
    ParameterizedTypeImpl outer = new ParameterizedTypeImpl(new Type[]{inner}, null, ResponseVO.class);
    return JSON.parseObject(result, outer);
  }

  public String getString(String url) {
    return get(url, defaultTimeout, null);
  }

  public String get(String url, int timeout, Map<String, Object> params) {
    try {
      HttpGet httpGet = createHttpGet(params, url);
      httpGet.setConfig(getRequestConfig(timeout, timeout));

      return getResponse(httpGet);
    } catch (Exception e) {
      log.error("exception：config:" + JSON.toJSONString(url), e);
    }
    return null;
  }

  private String getResponse(ClassicHttpRequest request) throws Exception {
    try (final CloseableHttpResponse response = client.execute(request)) {
      return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
    }
  }

  private HttpGet createHttpGet(Map<String, Object> params, String url) throws Exception {
    URL parsedUrl;
    if (ObjectUtils.isEmpty(params)) {
      parsedUrl = new URL(url);
    } else {
      List<NameValuePair> urlParams = new ArrayList<>(params.size() + 1);
      params.forEach((k, v) -> {
        if (!ObjectUtils.isEmpty(k) && !ObjectUtils.isEmpty(v)) {
          NameValuePair nameValuePair = new BasicNameValuePair(k, v.toString());
          urlParams.add(nameValuePair);
        }
      });
      parsedUrl = new URIBuilder(url).addParameters(urlParams).build().toURL();
    }
    String urlTem = parsedUrl.toString();
    HttpGet get = new HttpGet(urlTem);
    setHeader(get);
    return get;
  }

  private HttpPost createHttpPost(String url, ContentType contentType, String data) {
    HttpPost httpPost = new HttpPost(url);
    if (!ObjectUtils.isEmpty(data)) {
      HttpEntity entity = new StringEntity(data, contentType);
      httpPost.setEntity(entity);
    }
    setHeader(httpPost);
    return httpPost;
  }

  private void setHeader(HttpUriRequestBase requestBase) {
//    requestBase.setHeader(RequestID.REQUEST_ID_NAME, RequestID.getRequestID());
//    requestBase.setHeader(RequestID.USER_TOKEN_NAME, RequestID.getUserToken());
  }

}